Fork me on GitHub

Spring bean装配方式总结

Spring装配bean有三种主要的装配方式:

  • 在XML中进行显示配置
  • 在Java中进行显示配置
  • 隐式的bean发现机制与自动装配

关于依赖注入(DI)部分内容请去彭老斯的博客骚扰:浅析Spring的IoC和DI

以下案例均来自《spring实战(第4版)》第二章。截图均来自慕课视频Spring入门篇

其中,SgtPeppers为具体的bean,实现了CompactDisc接口,CDPLayer也为具体的bean,实现了MediaPlayer接口,但它需要依赖于SgtPeppers。CDPlayerConfig为配置类,用于JavaConfig方式或自动装配方式的部分配置,CDPlayerTest为测试类,用于测试是否装配成功。它们均在soundsystem包中。

前言

首先,什么是装配,书上的定义是:创建应用组件之间协作的行为。

在我的理解中,装配bean分为两个阶段,发现(注册)bean与连接bean(Spring注入),即spring IoC容器先要知道它将管理哪些bean,再将相互之间有关联的bean连接起来,如将一个bean的实例作为参数传给另一个bean的构造器或者setter方法等。

对于第一步,有两种方法,一是采用自动装配的@ComponentScan自动发现指定包下所有用@Component注解的bean,这种方法中,若未指定参数,则在当前包下寻找,反之则在作为参数的类所在的包下寻找,因此更优化的方式是在指定包下创建一个专门用来扫描的空接口。(当然,也可以采用XML配置context:component-scan标签)

mark

二是手动指定注册哪些bean,即在XML 中配置bean标签或者@Configuration所注解的类中用@bean指定。

例如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
@Configuration
//@ComponentScan
public class CDPlayerConfig {

@Bean
public CompactDisc sgtPeppers(){
return new SgtPeppers();
}

@Bean
public MediaPlayer cdPlayer(CompactDisc compactDisc){
return new CDPLayer(compactDisc);
}
}//实验证明@Configuration可加可不加,上述代码为基于JavaConfig配置,若改为自动装配则将两个方法全部注释掉并去掉@ComponentScan的注释,其余改动后面再说。

对于第二步,主要就是一般提及的三种情况:

自动装配

将需要被注册的bean用@Component注解,同时将需要被自动赋值的属性用@Autowired注解。这两个注解仅在自动装配中使用。

mark

例如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
@Component
public class CDPLayer implements MediaPlayer{
private CompactDisc cd;

@Autowired
public CDPLayer(CompactDisc cd) {
this.cd = cd;
}

public void play(){
System.out.print("啦啦啦 ");
cd.play();
}
}

其中,@Autowired可以注解在属性上、构造器上、setter方法上,甚至是任何方法上,只要该方法可以为属性赋值即可。需注意,该注解可以传入required属性值,默认为true,若改为false,可以避免因未给属性赋初始值而报的错,但在后面的语句中可能造成空指针异常。(@Autowired也可以用Java依赖注入规范中的@Inject替代,两者差别非常小)

值得一提的是,自动装配出的bean的ID就是将该类的名称的第一个大写字母变为小写,若想改变ID,可以在@Component中指定。(也可以使用Java依赖注入规范中的@Named(…)替代,两者差别非常小)

基于JavaConfig的装配

如第一段代码所示,用@Bean指定将要注册哪些bean,bean之间具体的连接得手动配置。当然,这里面还有些小技巧,例如:

1
2
3
4
5
6
7
8
9
@Bean
public MediaPlayer cdPlayer(CompactDisc compactDisc){
return new CDPLayer(compactDisc);
}

@Bean
public MediaPlayer cdPlayer(){
return new CDPLayer(sgtPeppers());
}

很明显,第一种方式的可重用性与可维护性高于第二种,因此我更倾向于使用接口来进行参数传递。

值得注意的是,带有@bean注解的方法可以采用任何必要的Java功能来产生bean实例,这里存在的可能性仅仅受到Java语言的限制。

另外,spring中默认bean都为单例,spring会拦截所有对它的调用,并确保直接返回该方法所创建的bean。

mark

mark

该方式装配出的bean的ID默认为方法名,若需修改,给@Bean的name参数赋值即可。

基于XML的装配

1
2
3
4
5
6
7
8
9
10
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

<bean id = "aaa" class = "soundsystem.SgtPeppers"/>
<bean id = "bbb" class = "soundsystem.CDPLayer">
<constructor-arg ref="aaa"/>
</bean>
</beans>

该方式中,bean标签用于注册bean,constructor-arg标签与property标签用于连接具有依赖关系的bean,也可以用于为属性赋字面量。

1
2
3
4
5
6
7
8
9
10
11
12
<bean id = "aaa" class = "aaa">
<constructor-arg ref = "bbb"/>
<constructor-arg value = "My name is Ben Wen"/>
<constructor-arg>
<!--若是set集合就用<set>标签,下同-->
<list>
<value>111</value>
<value>222</value>
</list>
</constructor-arg>
</bean>
<!--aaa类的构造器需要传3个参数,一个bbb类的实例,一个字符串,一个List集合-->
1
2
3
4
5
6
7
8
9
10
11
<bean id = "aaa" class = "aaa">
<property name = "xxx" ref = "bbb"/>
<property name = "yyy" value = "My name is Ben Wen"/>
<property name = "zzz">
<list>
<value>111</value>
<value>222</value>
</list>
</property>
</bean>
<!--为aaa类的成员变量xxx赋的初始值为bbb类的实例,为成员变量yyy赋的初始值为字符串,为成员变量zzz赋的初始值为一个List集合-->

当然,构造器注入与设值注入分别有对应的简写,即c-命名空间与p-命名空间,但它们不能传入集合,详见书。

此外,还有一种更实用的方式传入集合,及Spring util-命名空间,详见书。

mark


至此,三种常用的bean装配方式就说完了,总结起来就是,需要刻意去发现bean的只有自动装配一种方式,基于JavaConfig与XML的配置方式在注册bean的时候同时会将bean之间的依赖关系通过各自的方式手动表示出来。

-------------本文结束感谢您的阅读-------------
undefined